#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <assert.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/dir.h>
#include <sys/stat.h>

#include "fd_list.h"
#include "bstrlib.h"
#include "server.h"
#include "common.h"
#include "receive.h"
#include "send_file_listing.h"
#include "send_metadata.h"
#include "send_slice.h"

void receive_info(fd_list list, int i){
    int aux_recv=0, size=0, offset=0, amount=0, fd_client=0, j=0, port=0;
    bstring command, command_aux;
    struct bstrList *command_list;
    char *aux_buf = NULL, *buf_server = NULL, *aux = NULL, *end_ptr = NULL;
    /*cargo los nombres de los comandos en estructuras bstring para poder
    luego hacer las comparaciones*/

    bstring gfl = bfromcstr ("get_file_listing");
    bstring gm = bfromcstr ("get_metadata");
    bstring gs = bfromcstr ("get_slice");
    bstring q = bfromcstr ("quit");
    bstring h = bfromcstr ("hello");
    bstring nf = bfromcstr ("new_file");

    fd_client = fd_list_get_fd(list, i);
    assert(fd_client != 0);
    /*Creamos el buffer de lectura*/

    /*en buf_server vamos a tener el pedido del cliente, letter la usamos
    para guardar caracteres y aux_buf lo usamos para ir guardando lo que
    el cliente nos envia*/
    aux_buf = calloc(BUF_LENGTH, sizeof(char*));
    if(aux_buf == NULL){
        perror("malloc-aux_buf");
        bdestroy(gfl);
        bdestroy(gm);
        bdestroy(gs);
        bdestroy(q);
        bdestroy(h);
        bdestroy(nf);
        send_msg(fd_client, MSG199);
        close(fd_client);
        fd_list_remove(list, i);
        return;
    }
    /*En este bucle vamos a ir recibiendo del cliente hasta que nos
    mande un \r\n*/
    aux_recv = recv(fd_client, aux_buf, BUF_LENGTH, 0);
    if (aux_recv == -1){
        perror("recv");
    }
    if (aux_recv == 0){
        free(aux_buf);
        aux_buf=NULL;
        bdestroy(gfl);
        bdestroy(gm);
        bdestroy(gs);
        bdestroy(q);
        bdestroy(h);
        bdestroy(nf);
        close(fd_client);
        j = fd_list_item_get_relation(list, i);
        if (j != -1){
            /*Si entro aca, es porque el cliente que se me desconecto, tenía
            un servidor asociado, entonces lo desconecto*/
            #ifdef __DEBUG__
            printf("Perdi un cliente, desconecto el server relacionado\n");
            #endif
            j = fd_list_server_search(list, j);
            close(fd_list_get_server_fd(list, j));
            fd_list_server_remove(list, j);
        }
        fd_list_remove(list, i);
        return;
    }

    for(j=0;j<aux_recv;j++){
        if (aux_buf[j]=='\r'){
            if (aux_buf[j+1]=='\n'){
                fd_list_set_recv(list, i, true);
                j = j+2;
                break;
            }
            else{
                send_msg(fd_client, MSG100);
                free(aux_buf);
                aux_buf=NULL;
                bdestroy(gfl);
                bdestroy(gm);
                bdestroy(gs);
                bdestroy(q);
                bdestroy(h);
                bdestroy(nf);
                close(fd_client);
                fd_list_remove(list, i);
                return;
            }
        }
        else if (aux_buf[j]=='\n'){
            send_msg(fd_client, MSG100);
            free(aux_buf);
            aux_buf=NULL;
            bdestroy(gfl);
            bdestroy(gm);
            bdestroy(gs);
            bdestroy(q);
            bdestroy(h);
            bdestroy(nf);
            close(fd_client);
            fd_list_remove(list, i);
            return;
        }
    }

    fd_list_add_recv_str(list, i, aux_buf, j);

    if (fd_list_get_recv(list, i)){
        buf_server = fd_list_get_recv_str(list, i);
        /*creamos el bstring con todo lo que viene desde el cliente*/
        command = bfromcstr(buf_server);
        
        /*creamos una lista de bstrings separados por espacios*/
        command_list = bsplit(command, ' ');
        /*cargamos en esta variable solo el primer string 
        (ie comando a ejecutar)*/
        command_aux = ((command_list)->entry)[0];
    
        if ((command_list)->qty==1){
            size = blength (command_aux);
            bdelete (command_aux, size-2, 2);
        }

        if(fd_list_is_first_time(list, i) &&\
           bstrcmp(command_aux, h) != 0){
            /*Si entro aca es porque recibi cualquier cosa menos hello como
            primer comando. Cierro la conexion entonces*/
            send_msg(fd_list_get_fd(list, i), MSG102);
            close(fd_list_get_fd(list, i));
            fd_list_remove(list, i);
        }

        else{
            /*el comando pedido fue el get_file_listing*/
            if (bstrcmp(command_aux, gfl) == 0){
                /*Analizo que se haya ingresado el comando sin parametros
                adicionales*/
                if ((command_list->qty) != 1){
                    send_msg(fd_client, MSG201);
                }
                else{
                    /*en caso de que sea correcto, imprimo en el servidor el
                    comando recibido y llamo a la funcion pertinente*/
                    printf("Get File Listing\n");
                    fd_list_set_cmd(list, i, 0);
                    fd_list_set_finish(list, i, false);
                }
            }
            else if (bstrcmp(command_aux, gm) == 0){
                /*el comando pedido fue el get_metadata.
                Analizamos la correcta cantidad de parametros*/
                if ((command_list->qty) != 2){
                    send_msg(fd_client, MSG201);
                }
                else{
                    printf("Get Metadata\n");
                    fd_list_set_cmd(list, i, 1);
                    /*en caso de que sea correcto, imprimo en el servidor el
                    comando recibido y llamo a la funcion pertinente*/
                    size = blength (command_list->entry[1]);
                    bdelete (command_list->entry[1], size-2, 2);
                    aux = bdata(command_list->entry[1]);
                    fd_list_set_filename(list, i, aux);
                    fd_list_set_finish(list, i, false);
                    /*A continuacion elimino el \r\n del final del nombre
                    del archivo*/
                }
            }
            else if (bstrcmp(command_aux, gs) == 0){
                /*el comando pedido fue el get_slice.
                Analizamos la correcta cantidad de parametros*/
                if ((command_list->qty) != 4){
                    send_msg(fd_client, MSG201);
                }
                else {
                    /*Obtenemos el nombre del archivo y convertimos a enteros
                    los parametros adicionales (offset y amount)*/
                    aux = bdata(command_list->entry[1]);
                    fd_list_set_filename(list, i, aux);
                    aux = bdata(command_list->entry[2]);
                    offset = strtol(aux, &end_ptr, 10);
                    if (end_ptr == aux){
                        send_msg(fd_client, MSG201);
                    }
                    else{
                        offset = (int)offset;
                        fd_list_set_offset(list, i, offset);
                        aux = bdata(command_list->entry[3]);
                        amount = strtol(aux, &end_ptr, 10);
                        if (end_ptr == aux){
                            send_msg(fd_client, MSG201);
                        }
                        else{
                            amount = (int)amount;
                            fd_list_set_amount(list, i, amount);
                            fd_list_set_transferred(list, i, 0);
                            fd_list_set_finish(list, i, false);
                            printf("Get slice\n");
                            fd_list_set_cmd(list, i, 2);
                        }
                    }
                }
            }
            else if (bstrcmp(command_aux, q) == 0){
                /*el comando pedido fue el quit*/
                if ((command_list->qty) != 1){
                    send_msg(fd_client, MSG201);
                }
                else{
                    printf("Quit\n");
                    fd_list_set_cmd(list, i, 3);
                }
            }
            else if (bstrcmp(command_aux, h) == 0){
                /*el comando enviado fue el hello
                Analizamos la correcta cantidad de parametros*/
                if ((command_list->qty) != 2){
                    send_msg(fd_client, MSG201);
                }
                else{
                    printf("Hello\n");
                    /*en caso de que sea correcto, imprimo en el servidor el
                    comando recibido y llamo a la funcion pertinente*/
                    size = blength (command_list->entry[1]);
                    bdelete (command_list->entry[1], size-2, 2);
                    aux = bdata(command_list->entry[1]);
                    port = strtol(aux, &end_ptr, 10);
                    if (end_ptr == aux){
                        send_msg(fd_client, MSG201);
                    }
                    else{
                        port = (int)port;
                        fd_list_set_cmd(list, i, 5);
                        fd_list_set_item_port(list, i, port);
                        fd_list_set_finish(list, i, false);
                    }
                }
            }
            /*el comando pedido fue el new_file*/
            else if (bstrcmp(command_aux, nf) == 0){
                /*Analizo que se haya ingresado el comando sin parametros
                adicionales*/
                if ((command_list->qty) != 1){
                    send_msg(fd_client, MSG201);
                }
                else{
                    /*en caso de que sea correcto, imprimo en el servidor el
                    comando recibido y llamo a la funcion pertinente*/
                    printf("New File\n");
                    fd_list_set_cmd(list, i, 4);
                    fd_list_set_finish(list, i, false);
                }
            }
            else{
                /*en este caso habria que decir que el comando no se encuentra
                y evaluar la buena formacion del pedido dentro de los
                comandos anteriores*/
                printf("Comando no reconocido\n");
                send_msg(fd_client, MSG200);
            }
            
            fd_list_reset_recv_str(list, i);
            fd_list_set_recv(list, i, false);
        }
        free(buf_server);
        buf_server = NULL;
        bdestroy(command);
        bstrListDestroy(command_list);
    }
    /*Destruimos las estructuras usadas*/

    free(aux_buf);
    aux_buf=NULL;
    

    bdestroy(gfl);
    bdestroy(gm);
    bdestroy(gs);
    bdestroy(q);
    bdestroy(h);
    bdestroy(nf);
    return;
}

int receive_response(fd_list list, int i){
    int fd=0, j=0, aux_recv=0, response=0;
    char *aux_buf=NULL, *received=NULL, *end_ptr=NULL;

    fd = fd_list_get_server_fd(list, i);

    aux_buf = calloc(BUF_LENGTH, sizeof(char*));
    if(aux_buf == NULL){
        perror("malloc-aux_buf");
        send_msg(fd, MSG199);
        close(fd);
        fd_list_server_remove(list, i);
        return -1;
    }

    received = calloc(BUF_LENGTH, sizeof(char*));
    if(received == NULL){
        perror("malloc-received");
        send_msg(fd, MSG199);
        free(aux_buf);
        aux_buf = NULL;
        close(fd);
        fd_list_server_remove(list, i);
        return -1;
    }

    aux_recv = recv(fd, aux_buf, BUF_LENGTH, 0);
    if (aux_recv == -1){
        perror("recv");
        free(aux_buf);
        aux_buf = NULL;
        free(received);
        received = NULL;
        return -1;
    }

    if (aux_recv == 0){
        free(aux_buf);
        aux_buf = NULL;
        free(received);
        received = NULL;
        close(fd);
        fd_list_server_remove(list, i);
        return -1;
    }
    for(j=0;j<aux_recv;j++){
        if (aux_buf[j]==' '){
            break;
        }
        else{
            received[j]=aux_buf[j];
        }
    }
    response = strtol(received, &end_ptr, 10);
    if (end_ptr == received){
        send_msg(fd, MSG201);
        free(aux_buf);
        aux_buf = NULL;
        free(received);
        received = NULL;
        return -1;
    }
    else{
        response = (int)response;
    }
    free(aux_buf);
    aux_buf = NULL;
    free(received);
    received = NULL;
    #ifdef __DEBUG__
    printf("Respuesta %i\n", response);
    #endif
    return response;
}
